/****************************************************************************
*  Title: simple_edge.sc
*  Author: Bryant Smith
*  Date: 04/16/2011
*  Description: takes in an RGB image array over row by row over input c_queue
*			    and uses a simple alogrythm for edge detection then returns
*				the image to the an output c_queue once edge detection is 
*				performed

****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define EDGETHRESHOLD 40
// 3 is used to move array cord by 3 bytes since each pixel has 3 bytes (R G B)
#define ARRAYINIT  { {0,-3}, {-1,-3} ,{-1,0}, {-1,3}, {0,3}, {1,3}, {1,0}, {1,-3}  } 

// define image size, idea being from a hw camera this would be fixed
#define IMG_ROWS 512
#define IMG_CLMS 512

import "c_queue";


behavior receiveImage(i_receiver InQueue, out unsigned char image_buffer[IMG_ROWS][IMG_CLMS*3], 
		out unsigned char image8bpp[IMG_ROWS][IMG_CLMS])
{
	// image buffers these are currently dependent on the size and format of the image used
	unsigned char row_buffer_1[IMG_CLMS * 3]; // this file is 4x4 since there are 3 bytes/pixel -> 1536 bytes per row
	int row, a, j;

	void main(void) 
	{		
		
		printf("started edge detection\n");
		
		//********************
		// Load image from c_queue into memory
		//********************
		
		// read image from c_queue and load into image buffer
		for(row = 0; row < IMG_ROWS; row++) {

			// Load row with a row of the image
			InQueue.receive(row_buffer_1, (IMG_CLMS * 3));	
			
			// place row into image buffer
			for(a = 0; a < (IMG_CLMS * 3); a++){
				image_buffer[row][a] =  row_buffer_1[a]; 	
			}			
			
			// generate grayscale image matrix
			for(j = 0; j < (IMG_CLMS*3); j = j+3)
			{
				// create 8 bpp grayscale image with weight of .3R + .59G + .11B
				image8bpp[row][j/3] = (unsigned char) (0.3 * (row_buffer_1[j]) + 0.59 * (row_buffer_1[j+1]) + 0.11 * (row_buffer_1[j+2]));
			}
		}		
		printf("Finished reading image into memory.  Starting Edge Detection. . . .\n");
		
	}
};

behavior preProcessImage( in unsigned char image8bpp[IMG_ROWS][IMG_CLMS], out double diffxy[IMG_ROWS][IMG_CLMS])
{

	int masksize = 3;
	int convolveX[3][3] = {{-1, 0, 1}, {-1, 0, 1}, {-1, 0, 1}};
	int convolveY[3][3] = {{-1, -1, -1}, {0, 0, 0}, {1, 1, 1}};
	int x, y, x1, y1, posx, posy, imPosx, imPosy;
	int width, height;

	double diffx[IMG_ROWS][IMG_CLMS];
	double diffy[IMG_ROWS][IMG_CLMS];		
	double valx, valy;

	void main(void) 
	{
	
		width = IMG_CLMS;
		height = IMG_ROWS;
		//********************
		// Apply x, y, xy gradient convolution
		//********************			
		for(x = masksize / 2; x < width - (masksize / 2); x++) {  //columns
			for(y= masksize / 2; y < height- (masksize / 2); y++) { // rows
				valx = 0;
				valy = 0;
				for( x1 = 0; x1 < masksize; x1++) {
					for( y1 = 0; y1 < masksize; y1++) {
						imPosx = (x + (x1 - (masksize / 2)));
						imPosy = (y + (y1 - (masksize / 2)));
						
						valx += ((image8bpp[imPosy][imPosx]) * convolveX[y1][x1]);
						valy += ((image8bpp[imPosy][imPosx]) * convolveY[y1][x1]);
					}
				}
				diffx[y][x] = valx * valx;  // horizontial gradient
				diffy[y][x] = valy * valy;  // vertical gradient
				diffxy[y][x] =  valx * valy;  // xy gradient
			}
		}	
	}
};

behavior edgeDetection( in unsigned char image_buffer[IMG_ROWS][IMG_CLMS * 3], i_sender OutQueue)
{
	// image buffers these are currently dependent on the size and format of the image used
	unsigned char edge_buffer[IMG_ROWS][IMG_CLMS * 3];  // this can be simplified to save memory, carefully - can update adjecent rows
			
	// edge detection threshold
	int threshold = EDGETHRESHOLD;
	
	// const adjaent cord matrix
	int adj_cord[8][2] = ARRAYINIT;
	
	// detection variables
	int i, j, k, x, y;
	double min, max, luma;

	void main(void) 
	{		
		//********************
		// Perform edge detection
		//********************
		// can delay this loop by 1 row, wait for row from c_queue then perform edge detection on the previous row
		// then wait for the next row until the last row is received, then perform one more edge detection
		// and send event to monitor to close file and exit.		

		for(i=0 ; i < 512; i++)	  
		{ 
			for(j = 0; j < (IMG_CLMS * 3); j = j+3){
				min = -1; 
				max = -1; 

				for(k=0;k<8;k++) {				
									
					x = i + adj_cord[k][0]; 
					y = j + adj_cord[k][1]; 

					// check if adjacent pixels are outside of image matrix
					if(x < 0 || y < 0 || x >= IMG_ROWS || y >= (IMG_CLMS * 3)) // 2110 here because we need to account for 3 bytes per pixel
					{
						// do nothing
					} else {
						// need to update to convert char to int here on various systems
						//luma = (int) 0.215366 * (image_buffer[x][y] & 0x000000FF) + 0.7152 * (image_buffer[x][y+1]& 0x000000FF) + 0.0722 * (image_buffer[x][y+2]& 0x000000FF);
						luma =  0.2155 * (image_buffer[x][y]) + 0.7152 * (image_buffer[x][y+1]) + 0.0723 * (image_buffer[x][y+2]);

						
						if(min == -1 || luma < min) 
						{
							min = luma; 
						}
						
						if(max == -1 || luma > max) 
						{
							max = luma; 
						}
					}
				}

				if( (max - min) > threshold) { 
					// if edge detected paint red, otherwise copy, come back to save memory here wait to update previous row
					edge_buffer[i][j] = 0x00;
					edge_buffer[i][j+1] = 0x00;
					edge_buffer[i][j+2] = 0xFF;
				} else {
					edge_buffer[i][j] = image_buffer[i][j];
					edge_buffer[i][j+1] = image_buffer[i][j+1];
					edge_buffer[i][j+2] = image_buffer[i][j+2];
				}					
			} 
			
			// send edge detected row here
			OutQueue.send(edge_buffer[i], (IMG_CLMS * 3));
		}
		printf("Completed edge detection\n");
	}	
};

// send image array for debuging
behavior debugSender(in double diffxy[IMG_ROWS][IMG_CLMS]) 
{
	// File names
	char OUTPUT_BMP[] = "output8bpp.bmp"; // output file name
	
	int row, wt_cnt, i, j;
	unsigned char pixel;
	double val, max, min;
		
	void main(void) {
		
		//File variable
		FILE * wptr;
		
		// normalize not yet used 
		// find min an max value
		val = 0;
		max = 0;
		min = 0;
		for(i = 0; i < IMG_ROWS; i++) 
		{
			for(j = 0; j < IMG_CLMS; j++)
			{
				val = diffxy[i][j];
				
				if (val > max) max = val;
				if (val < min) min = val;
			}
		}
  
		// open output file file
		wptr = fopen(OUTPUT_BMP, "a"); // append to file which already has header from stimulus
  
		waitfor(300);  // this is needed so simulator does not think wait period during image processing is a deadlock
				// can removed when FSM events are added
		printf("Finished wait on debug monitor\n");
  
		//waitfor(30);
		for(row = 0; row < IMG_ROWS; row++)
		{
			
			for(wt_cnt = 0; wt_cnt < IMG_CLMS; wt_cnt++)
			{
				//pixel = diffxy[row][wt_cnt];
				pixel = (unsigned char) ((diffxy[row][wt_cnt]) / 765) + 128;  // normalize to unsigned char
				fprintf(wptr,"%c",pixel);                                    // here worst case is +/-765
				fprintf(wptr,"%c",pixel);
				fprintf(wptr,"%c",pixel);
			}
		}
  
	fclose(wptr);
	printf("Processing completed 8bpp image check file %s\n", OUTPUT_BMP);
	exit(0);
	}
};

behavior simple_edge(i_receiver InQueue, i_sender OutQueue)
{
	// image buffers these are currently dependent on the size and format of the image used
	unsigned char img_buf[IMG_ROWS][IMG_CLMS * 3]; //char image buffer
	unsigned char img8bpp[IMG_ROWS][IMG_CLMS]; // 8bpp image matrix
	double diffxy[IMG_ROWS][IMG_CLMS];
	
	
	receiveImage    imgRcvr(InQueue, img_buf, img8bpp);
	preProcessImage preProc(img8bpp, diffxy);
	edgeDetection   edgeDet(img_buf, OutQueue);
	debugSender     dbgSndr(diffxy);

	void main(void) 
	{	
		imgRcvr.main();
		edgeDet.main();
		preProc.main();
		dbgSndr.main();
	}
	
};
